package com.maychat.service.impl;

import com.maychat.config.AppConfig;
import com.maychat.config.Consts;
import com.maychat.enums.ContactType;
import com.maychat.enums.MessageStatus;
import com.maychat.enums.MessageType;
import com.maychat.exception.BusinessException;
import com.maychat.mapper.ChatMessageMapper;
import com.maychat.mapper.ChatSessionUserMapper;
import com.maychat.pojo.ChatMessage;
import com.maychat.pojo.ChatSession;
import com.maychat.pojo.MayChatSystemOptions;
import com.maychat.pojo.UserInfoToken;
import com.maychat.pojo.extended.Message;
import com.maychat.service.ChatMessageService;
import com.maychat.utils.StringTools;
import com.maychat.websocket.MessageHandler;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.core.ApplicationContext;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;

@Slf4j
@Service
public class ChatMessageServiceImpl implements ChatMessageService {
    @Autowired
    RedisServiceImpl redisService;

    @Autowired
    private ChatSessionUserMapper chatSessionUserMapper;

    @Autowired
    private ChatMessageMapper chatMessageMapper;

    @Autowired
    private MessageHandler messageHandler;

    @Autowired
    private AppConfig appConfig;

    @Autowired
    HttpServletResponse resp;

    @Lazy
    @Autowired
    private ChatMessageService chatMessageService;

    @Resource
    private ChatClient chatClient;

    @Async
    public void saveMessage4RobotAsync(String userId, String content){
        chatMessageService.saveMessage4Robot(userId, content);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Message<?> saveMessage(ChatMessage chatMessage, UserInfoToken tokenObj) throws BusinessException {
        // 若不是机器人，判断好友状态
        if (!(Consts.ROBOT_UID.equals(tokenObj.getUserId()) ||
                Consts.ROBOT_UID.equals(chatMessage.getContactId()))) {
            List<String> userContacts = redisService.getUserContacts(tokenObj.getUserId());
            boolean contains = userContacts.contains(chatMessage.getContactId());
            if (!contains) {
                if (chatMessage.getContactType() == ContactType.USER.ordinal())
                    throw new BusinessException("对方不是你的好友");
                else
                    throw new BusinessException("你不在群聊中");
            }
        }
        String userId = tokenObj.getUserId();
        String contactId = chatMessage.getContactId();
        ContactType type = ContactType.getByPrefix(contactId);
        String sessionId;
        if (type == ContactType.USER)
            sessionId = StringTools.generateSessionId(userId, contactId);
        else
            sessionId = StringTools.generateGroupSessionId(contactId);
        chatMessage.setSessionId(sessionId);

        long t = System.currentTimeMillis();
        chatMessage.setSendTime(t);

        chatMessage.setStatus(chatMessage.getMessageType() == MessageType.MEDIA_CHAT.ordinal() ? MessageStatus.SENDING.ordinal() : MessageStatus.SENT.ordinal());

//        chatMessage.setMessageContent(StringTools.escapeHTML(chatMessage.getMessageContent()));
        chatMessage.setMessageContent(chatMessage.getMessageContent());

        // 更新会话
        ChatSession chatSession = new ChatSession();
        chatSession.setLastMessage(chatMessage.getMessageContent());
        if (type == ContactType.GROUP)
            chatSession.setLastMessage(tokenObj.getNickName() + ":" + chatMessage.getMessageContent());
        chatSession.setLastReceiveTime(t);
        chatSession.setSessionId(sessionId);
        chatSessionUserMapper.updateChatSessionBySessionId(chatSession);
        // 保存到消息表
        chatMessage.setSendUserId(userId);
        chatMessage.setSendUserNickName(tokenObj.getNickName());
        chatMessage.setContactType(type.ordinal());
        chatMessageMapper.insertChatMessage(chatMessage);

        Message<Object> msg = new Message<>();
        BeanUtils.copyProperties(chatMessage, msg);

        if (!Consts.ROBOT_UID.equals(msg.getContactId()))
            messageHandler.sendMsg(msg);
        return msg;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveMessage4Robot(String userId, String userMsg) {
        // TODO 预留对接AI
        String aiResp = chatClient
                .prompt()
                .user(userMsg)
                .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, userId))
                .call()
                .content();
        MayChatSystemOptions systemOptions = redisService.getSystemOptions();
        UserInfoToken robotToken = UserInfoToken.getRobotToken(systemOptions);
        ChatMessage robotChatMessage = new ChatMessage();
        robotChatMessage.setSendUserId(systemOptions.getRobotUId());
        robotChatMessage.setSendUserNickName(systemOptions.getRobotNickName());
        robotChatMessage.setContactId(userId);
        robotChatMessage.setContactType(ContactType.USER.ordinal());
        robotChatMessage.setMessageContent(aiResp);
        robotChatMessage.setMessageType(MessageType.CHAT.ordinal());
        saveMessage(robotChatMessage, robotToken);
    }

    @Override
    public void uploadFile(String userId, Long messageId, MultipartFile file, MultipartFile cover) throws BusinessException, IOException {
        ChatMessage chatMessage = chatMessageMapper.selectById(messageId);
        if (chatMessage == null || !chatMessage.getSendUserId().equals(userId)) throw new BusinessException();
        MayChatSystemOptions systemOptions = redisService.getSystemOptions();
        String extName = StringTools.getExtNameWithCheck(chatMessage.getFileName(), file.getSize(), systemOptions);
        if (extName == null) throw new BusinessException("超出规定大小");
        String yearMonth = StringTools.getYearMonth(chatMessage.getSendTime());
        String path = appConfig.getProjectFolder() + Consts.FOLDER_FILE + "/" + yearMonth;
        File fileTest = new File(path);
        if (!fileTest.exists()) fileTest.mkdirs();
        file.transferTo(new File(path + "/" + messageId + extName));
        if (cover != null)
            cover.transferTo(new File(path + "/" + messageId + extName + Consts.IMAGE_SMALL_SUFFIX));
        ChatMessage updateChatMessage = new ChatMessage();
        updateChatMessage.setStatus(MessageStatus.SENT.ordinal());
        updateChatMessage.setMessageId(messageId);
        chatMessageMapper.updateById(updateChatMessage);
        Message<Object> msg = new Message<>();
        msg.setStatus(MessageStatus.SENT.ordinal());
        msg.setMessageId(messageId);
        msg.setMessageType(MessageType.FILE_UPLOAD.ordinal());
        msg.setContactId(chatMessage.getContactId());
        messageHandler.sendMsg(msg);
    }

    /**
     * @return 第一个强转为String文件名，第二个强转为File文件对象（不要用file对象的文件名属性）,第三个转为StringMIME
     */
    @Override
    public Object[] download(UserInfoToken tokenObj, Long messageId, Boolean wantCover) throws BusinessException, FileNotFoundException {
        ChatMessage chatMessage = chatMessageMapper.selectById(messageId);
        ContactType type = ContactType.getByOrdinal(chatMessage.getContactType());
        if (type == ContactType.USER && !tokenObj.getUserId().equals(chatMessage.getContactId()))
            throw new BusinessException();
        String yearMonth = StringTools.getYearMonth(chatMessage.getSendTime());
        String realFilePath = appConfig.getProjectFolder() + Consts.FOLDER_FILE + "/" + yearMonth + "/" + messageId;
        String extName = StringTools.getExtName(chatMessage.getFileName());
        realFilePath += extName;
        if (wantCover) realFilePath += Consts.IMAGE_SMALL_SUFFIX;
        File fileObj = new File(realFilePath);
        if (!fileObj.exists()) {
            log.error("文件不存在{}", realFilePath);
            throw new FileNotFoundException("文件不存在");
        }
        return new Object[]{chatMessage.getFileName(), fileObj, StringTools.getMimeType(extName)};
    }
}
